home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / admin / shadow-9.tar / shadow-9 / shadow-960129 / smain.c < prev    next >
C/C++ Source or Header  |  1995-12-17  |  14KB  |  581 lines

  1. /*
  2.  * Copyright 1989 - 1994, John F. Haugh II
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by John F. Haugh, II
  16.  *      and other contributors.
  17.  * 4. Neither the name of John F. Haugh, II nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY JOHN HAUGH AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL JOHN HAUGH OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #include <sys/types.h>
  35. #include <stdio.h>
  36.  
  37. #ifndef    lint
  38. static char rcsid[] = "$Id: smain.c,v 1.3 1995/12/17 03:13:46 marekm Exp $";
  39. #endif
  40.  
  41. /*
  42.  * Set up some BSD defines so that all the BSD ifdef's are
  43.  * kept right here 
  44.  */
  45.  
  46. #include "config.h"
  47. #include "prototypes.h"
  48. #include "defines.h"
  49. #if defined(USG) || defined(SUN4) || defined(__linux__)
  50. #include <sys/ioctl.h>
  51. #include <termio.h>
  52. #else
  53. #include <sgtty.h>
  54. #endif
  55.  
  56. #include <grp.h>
  57. #include <signal.h>
  58. #include "lastlog.h"
  59. #include "pwd.h"
  60. #ifdef    SHADOWPWD
  61. #include "shadow.h"
  62. #endif
  63. #include "pwauth.h"
  64.  
  65. #ifdef    USE_SYSLOG
  66. #include <syslog.h>
  67.  
  68. #ifndef    __linux__
  69. /*VARARGS*/ int syslog();
  70. #endif
  71.  
  72. #ifndef    LOG_WARN
  73. #define    LOG_WARN LOG_WARNING
  74. #endif    /* !LOG_WARN */
  75. #endif    /* USE_SYSLOG */
  76.  
  77. static    char    *NOT_WHEEL = "You are not authorized to su %s\n";
  78.  
  79. /*
  80.  * Password aging constants
  81.  *
  82.  *    DAY - seconds in a day
  83.  *    WEEK - seconds in a week
  84.  *    SCALE - convert from clock to aging units
  85.  */
  86.  
  87. #define    DAY    (24L*3600L)
  88. #define    WEEK    (7L*DAY)
  89.  
  90. #ifdef    ITI_AGING
  91. #define    SCALE    (1)
  92. #else
  93. #define    SCALE    DAY
  94. #endif
  95.  
  96. /*
  97.  * Assorted #defines to control su's behavior
  98.  */
  99.  
  100. #ifndef    MAXENV
  101. #define    MAXENV    128
  102. #endif
  103.  
  104. /*
  105.  * Global variables
  106.  */
  107.  
  108. char    hush[BUFSIZ];
  109. char    name[BUFSIZ];
  110. char    pass[BUFSIZ];
  111. char    home[BUFSIZ];
  112. char    prog[BUFSIZ];
  113. char    mail[BUFSIZ];
  114. char    oldname[BUFSIZ];
  115. char    *newenvp[MAXENV];
  116. char    *Prog;
  117. int    newenvc = 0;
  118. int    maxenv = MAXENV;
  119. struct    passwd    pwent;
  120.  
  121. /*
  122.  * External identifiers
  123.  */
  124.  
  125. extern    void    addenv ();
  126. extern    void    entry ();
  127. extern    void    sulog ();
  128. extern    void    subsystem ();
  129. extern    void    setup ();
  130. extern    void    motd ();
  131. extern    void    mailcheck ();
  132. extern    void    shell ();
  133. extern    char    *ttyname ();
  134. extern    char    *getenv ();
  135. extern    char    *getpass ();
  136. extern    char    *tz ();
  137. extern    char    *pw_encrypt();
  138. extern    int    pw_auth();
  139. #ifndef __STDC__
  140. extern    struct    passwd    *getpwuid ();
  141. extern    struct    passwd    *getpwnam ();
  142. #ifdef    SHADOWPWD
  143. extern    struct    spwd    *getspnam ();
  144. #endif
  145. #endif    /* !__STDC__ */
  146. extern    char    *getdef_str();
  147. extern    int    getdef_bool();
  148. extern    char    **environ;
  149.  
  150. /*
  151.  * die - set or reset termio modes.
  152.  *
  153.  *    die() is called before processing begins.  signal() is then
  154.  *    called with die() as the signal handler.  If signal later
  155.  *    calls die() with a signal number, the terminal modes are
  156.  *    then reset.
  157.  */
  158.  
  159. SIGTYPE
  160. #if defined(__STDC__)
  161. die (int killed)
  162. #else
  163. die (killed)
  164. int    killed;
  165. #endif
  166. {
  167. #if defined(BSD) || defined(SUN)
  168.     static    struct    sgttyb    sgtty;
  169.  
  170.     if (killed)
  171.         stty (0, &sgtty);
  172.     else
  173.         gtty (0, &sgtty);
  174. #else
  175.     static    struct    termio    sgtty;
  176.  
  177.     if (killed)
  178.         ioctl (0, TCSETA, &sgtty);
  179.     else
  180.         ioctl (0, TCGETA, &sgtty);
  181. #endif
  182.     if (killed) {
  183. #ifdef    USE_SYSLOG
  184.         closelog ();
  185. #endif
  186.         exit (killed);
  187.     }
  188. }
  189.  
  190. int
  191. iswheel(name)
  192.     char *name;
  193. {
  194.     struct group *grp;
  195.     char **p;
  196.  
  197.     grp = getgrgid(0);
  198.     if (!grp || !grp->gr_mem)
  199.         return 0;
  200.     for (p = &grp->gr_mem[0]; *p; p++)
  201.         if (!strcmp(name, *p))
  202.             return 1;
  203.     return 0;
  204. }
  205.  
  206. /*
  207.  * su - switch user id
  208.  *
  209.  *    su changes the user's ids to the values for the specified user.
  210.  *    if no new user name is specified, "root" is used by default.
  211.  *
  212.  *    The only valid option is a "-" character, which is interpreted
  213.  *    as requiring a new login session to be simulated.
  214.  *
  215.  *    Any additional arguments are passed to the user's shell.  In
  216.  *    particular, the argument "-c" will cause the next argument to
  217.  *    be interpreted as a command by the common shell programs.
  218.  */
  219.  
  220. int    main (argc, argv, envp)
  221. int    argc;
  222. char    **argv;
  223. char    **envp;
  224. {
  225.     SIGTYPE    (*oldsig)();
  226.     char    *cp;
  227.     char    arg0[64];
  228.     char    *tty = 0;        /* Name of tty SU is run from        */
  229.     int    doshell = 0;
  230.     int    fakelogin = 0;
  231.     int    amroot = 0;
  232.     int    my_uid;
  233.     struct    passwd    *pw = 0;
  234. #ifdef    SHADOWPWD
  235.     struct    spwd    *spwd = 0;
  236. #endif
  237.  
  238.     /*
  239.      * Get the program name.  The program name is used as a
  240.      * prefix to most error messages.  It is also used as input
  241.      * to the openlog() function for error logging.
  242.      */
  243.  
  244.     Prog = basename(argv[0]);
  245.  
  246. #ifdef    USE_SYSLOG
  247.     openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  248. #endif
  249.  
  250.     /*
  251.      * Get the tty name.  Entries will be logged indicating that
  252.      * the user tried to change to the named new user from the
  253.      * current terminal.
  254.      */
  255.  
  256.     if (isatty (0) && (cp = ttyname (0))) {
  257.         if (strncmp (cp, "/dev/", 5) == 0)
  258.             tty = cp + 5;
  259.         else
  260.             tty = cp;
  261.     } else
  262.         tty = "???";
  263.  
  264.     /*
  265.      * Process the command line arguments. 
  266.      */
  267.  
  268.     argc--; argv++;            /* shift out command name */
  269.  
  270.     if (argc > 0 && argv[0][0] == '-' && argv[0][1] == '\0') {
  271.         fakelogin = 1;
  272.         argc--; argv++;        /* shift ... */
  273.     }
  274.  
  275.     /*
  276.      * If a new login is being set up, the old environment will
  277.      * be ignored and a new one created later on.
  278.      */
  279.  
  280.     if (! fakelogin)
  281.         while (*envp)
  282.             addenv (*envp++);
  283.  
  284.     if (fakelogin && (cp=getdef_str("ENV_TZ")))
  285.         addenv (*cp == '/' ? tz(cp) : cp);
  286.  
  287.     /*
  288.      * The clock frequency will be reset to the login value if required
  289.      */
  290.  
  291.     if (fakelogin && (cp=getdef_str("ENV_HZ")) )
  292.         addenv (cp);        /* set the default $HZ, if one */
  293.  
  294.     /*
  295.      * The terminal type will be left alone if it is present in the
  296.      * environment already.
  297.      */
  298.  
  299.     if (fakelogin && (cp = getenv ("TERM"))) {
  300.         char    term[BUFSIZ];
  301.  
  302.         if (strlen(cp) + 5 < sizeof term) {
  303.             strcpy(term, "TERM=");
  304.             strcat(term, cp);
  305.             addenv(term);
  306.         }
  307.     }
  308.  
  309.     /*
  310.      * The next argument must be either a user ID, or some flag to
  311.      * a subshell.  Pretty sticky since you can't have an argument
  312.      * which doesn't start with a "-" unless you specify the new user
  313.      * name.  Any remaining arguments will be passed to the user's
  314.      * login shell.
  315.      */
  316.  
  317.     if (argc > 0 && argv[0][0] != '-') {
  318.         STRFCPY(name, argv[0]);    /* use this login id */
  319.         argc--; argv++;        /* shift ... */
  320.     }
  321.     if (! name[0])             /* use default user ID */
  322.         (void) strcpy (name, "root");
  323.  
  324.     doshell = argc == 0;        /* any arguments remaining? */
  325.  
  326.     /*
  327.      * Get the user's real name.  The current UID is used to determine
  328.      * who has executed su.  That user ID must exist.
  329.      */
  330.  
  331.     /* Changed to try getlogin() first and use the old method only if
  332.        getlogin() fails.  Should handle shared uid better.  --marekm */
  333.  
  334.     my_uid = getuid();
  335.     amroot = (my_uid == 0);
  336.     cp = getlogin();
  337.     pw = cp ? getpwnam(cp) : getpwuid(my_uid);
  338.     if (!pw) {
  339. #ifdef    USE_SYSLOG
  340.         syslog (LOG_CRIT, "Unknown UID: %d (%s)\n",
  341.             my_uid, cp ? cp : "???");
  342. #endif
  343.         goto failure;
  344.     }
  345.     STRFCPY(oldname, pw->pw_name);
  346.  
  347. top:
  348.     /*
  349.      * This is the common point for validating a user whose name
  350.      * is known.  It will be reached either by normal processing,
  351.      * or if the user is to be logged into a subsystem root.
  352.      *
  353.      * The password file entries for the user is gotten and the
  354.      * account validated.
  355.      */
  356.  
  357.     if ((pw = getpwnam (name))) {
  358. #ifdef    SHADOWPWD
  359.         if ((spwd = getspnam (name)))
  360.             pw->pw_passwd = spwd->sp_pwdp;
  361. #else
  362.         ;
  363. #endif
  364.     } else {
  365.         (void) fprintf (stderr, "Unknown id: %s\n", name);
  366. #ifdef    USE_SYSLOG
  367.         closelog ();
  368. #endif
  369.         exit (1);
  370.     }
  371.     pwent = *pw;
  372.  
  373.     /*
  374.      * BSD systems only allow "wheel" to SU to root.  USG systems
  375.      * don't, so we make this a configurable option.
  376.      */
  377.  
  378.     /* The original Shadow 3.3.2 did this differently.  Do it like BSD:
  379.  
  380.        - check for uid 0 instead of name "root" - there are systems
  381.        with several root accounts under different names,
  382.  
  383.        - check the contents of /etc/group instead of the current group
  384.        set (if the group is not empty, you must be listed as a member,
  385.        primary group id 0 is not sufficient).
  386.  
  387.        TODO:
  388.        FreeBSD su can be compiled to ask the current user's password
  389.        instead of the root password if the user is a member of the
  390.        wheel group.  This might be good for large systems with more
  391.        than one administrator, so that they don't have to share the
  392.        same root password and remember two passwords...  --marekm */
  393.  
  394.     if (!amroot && pwent.pw_uid == 0 && getdef_bool("SU_WHEEL_ONLY")
  395.         && !iswheel(oldname)) {
  396.         fprintf(stderr, NOT_WHEEL, name);
  397.         exit(1);
  398.     }
  399.  
  400.     /*
  401.      * Set the default shell.
  402.      */
  403.  
  404.     if (pwent.pw_shell[0] == '\0')
  405.         pwent.pw_shell = "/bin/sh";
  406.  
  407.     /*
  408.      * Set up a signal handler in case the user types QUIT.
  409.      */
  410.  
  411.     die (0);
  412.     oldsig = signal (SIGQUIT, die);
  413.  
  414.     /*
  415.      * See if the system defined authentication method is being used.
  416.      * The first character of an administrator defined method is an
  417.      * '@' character.
  418.      */
  419.  
  420.     if (! amroot && pw_auth (pwent.pw_passwd, name, PW_SU, (char *) 0)) {
  421. #ifdef    USE_SYSLOG
  422.         syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
  423.             "Authentication failed for %s\n", name);
  424. #endif
  425. failure:
  426.         sulog (tty, 0);        /* log failed attempt */
  427.         puts ("Sorry.");
  428. #ifdef    USE_SYSLOG
  429.         if ( getdef_bool("SYSLOG_SU_ENAB") )
  430.             syslog (pwent.pw_uid ? LOG_INFO:LOG_CRIT,
  431.                 "- %s %s-%s\n", tty,
  432.                 oldname[0] ? oldname:"???",
  433.                 name[0] ? name:"???");
  434.         closelog ();
  435. #endif
  436.         exit (1);
  437.     }
  438.     (void) signal (SIGQUIT, oldsig);
  439.  
  440.     /*
  441.      * Check to see if the account is expired.  root gets to
  442.      * ignore any expired accounts, but normal users can't become
  443.      * a user with an expired password.
  444.      */
  445.  
  446.     if (! amroot) {
  447. #ifdef    SHADOWPWD
  448.         if (spwd) {
  449.             if (isexpired (&pwent, spwd)) {
  450. #ifdef    USE_SYSLOG
  451.                 syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
  452.                     "Expired account %s\n", name);
  453. #endif
  454.                 goto failure;
  455.             }
  456.         }
  457. #else
  458.         if (0)
  459.             ;    /* There is no password to be expired */
  460. #endif
  461. #if defined(ATT_AGE) && defined(AGING)
  462.         else if (pwent.pw_age[0] &&
  463.                 isexpired (&pwent)) {
  464. #ifdef    USE_SYSLOG
  465.             syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
  466.                 "Expired account %s\n", name);
  467. #endif
  468.             goto failure;
  469.         }
  470. #endif    /* ATT_AGE */
  471.     }
  472.  
  473.     /*
  474.      * Check to see if the account permits "su".  root gets to
  475.      * ignore any restricted accounts, but normal users can't become
  476.      * a user if there is a "SU" entry in the /etc/porttime file
  477.      * denying access to the account.
  478.      */
  479.  
  480.     if (! amroot) {
  481.         if (! isttytime (pwent.pw_name, "SU", time ((time_t *) 0))) {
  482. #ifdef    USE_SYSLOG
  483.             syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
  484.                 "SU by %s to restricted account %s\n",
  485.                     oldname, name);
  486. #endif
  487.             goto failure;
  488.         }
  489.     }
  490.  
  491.     cp = getdef_str( pwent.pw_uid == 0 ? "ENV_SUPATH" : "ENV_PATH" );
  492.     addenv( cp != NULL ? cp : "PATH=/bin:/usr/bin" );
  493.  
  494.     environ = newenvp;        /* make new environment active */
  495.  
  496.     if (getenv ("IFS"))        /* don't export user IFS ... */
  497.         addenv ("IFS= \t\n");    /* ... instead, set a safe IFS */
  498.  
  499.     if (pwent.pw_shell[0] == '*') { /* subsystem root required */
  500.         subsystem (&pwent);    /* figure out what to execute */
  501.         endpwent ();
  502. #ifdef SHADOWPWD
  503.         endspent ();
  504. #endif
  505.         goto top;
  506.     }
  507.  
  508.     sulog (tty, 1);            /* save SU information */
  509.     endpwent ();
  510. #ifdef SHADOWPWD
  511.     endspent ();
  512. #endif
  513. #ifdef    USE_SYSLOG
  514.     if ( getdef_bool("SYSLOG_SU_ENAB") )
  515.         syslog (LOG_INFO, "+ %s %s-%s\n", tty,
  516.             oldname[0] ? oldname:"???", name[0] ? name:"???");
  517. #endif
  518.     if (fakelogin)
  519.         setup (&pwent);        /* set UID, GID, HOME, etc ... */
  520.     else {
  521.         if (setgid (pwent.pw_gid) || setuid (pwent.pw_uid))  {
  522.             perror ("Can't set ID");
  523. #ifdef    USE_SYSLOG
  524.             syslog (LOG_CRIT, "Unable to set uid = %d, gid = %d\n",
  525.                 pwent.pw_uid, pwent.pw_gid);
  526.             closelog ();
  527. #endif
  528.             exit (1);
  529.         }
  530.     }
  531.  
  532.     /*
  533.      * See if the user has extra arguments on the command line.  In
  534.      * that case they will be provided to the new user's shell as
  535.      * arguments.
  536.      */
  537.  
  538.     if (! doshell) {
  539.  
  540.         /*
  541.          * Use new user's shell from /etc/passwd and create an
  542.          * argv with the rest of the command line included.
  543.          */
  544.  
  545.         argv[-1] = pwent.pw_shell;
  546.         (void) execv (pwent.pw_shell, &argv[-1]);
  547.         (void) fprintf (stderr, "No shell\n");
  548. #ifdef    USE_SYSLOG
  549.         syslog (LOG_WARN, "Cannot execute %s\n", pwent.pw_shell);
  550.         closelog ();
  551. #endif
  552.         exit (1);
  553.     }
  554.     if (fakelogin) {
  555.         if (! hushed (&pwent)) {
  556.             motd ();
  557.             mailcheck ();
  558.         }
  559.         cp = getdef_str("SU_NAME");
  560.         if (!cp)
  561.             cp = basename(pwent.pw_shell);
  562.  
  563.         arg0[0] = '-';
  564.         strncpy(arg0+1, cp, sizeof(arg0)-2);
  565.         arg0[sizeof(arg0)-1] = '\0';
  566.         cp = arg0;
  567.     } else
  568.         cp = basename(pwent.pw_shell);
  569.  
  570.     shell (pwent.pw_shell, cp);
  571. #if 0  /* This doesn't work anyway, shell() never returns...  --marekm */
  572. #ifdef    USE_SYSLOG
  573.     syslog (LOG_WARN, "Cannot execute %s\n", pwent.pw_shell);
  574.     closelog ();
  575. #endif
  576.     exit (1);
  577. #endif
  578.     /*NOTREACHED*/
  579.     return 1;
  580. }
  581.